package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.list.dao.GoodsRepository;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuAttrValue;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.ml.job.results.Bucket;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.range.Range;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class SearchServiceImpl implements SearchService {


    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public void upperGoods(Long skuId) {

        Goods goods = new Goods();
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);

        goods.setId(skuInfo.getId());
        goods.setTitle(skuInfo.getSkuName());
        goods.setCreateTime(new Date());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());

        BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
        goods.setCategory1Id(categoryView.getCategory1Id());
        goods.setCategory1Name(categoryView.getCategory1Name());
        goods.setCategory2Id(categoryView.getCategory2Id());
        goods.setCategory2Name(categoryView.getCategory2Name());
        goods.setCategory3Id(categoryView.getCategory3Id());
        goods.setCategory3Name(categoryView.getCategory3Name());

        BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());

        goods.setTmId(trademark.getId());
        goods.setTmName(trademark.getTmName());

        List<SkuAttrValue> attrList = productFeignClient.getAttrList(skuId);

        List<SearchAttr> searchAttrList = attrList.stream().map(sav -> {
            SearchAttr searchAttr = new SearchAttr();
            searchAttr.setAttrId(sav.getAttrId());
            searchAttr.setAttrName(sav.getBaseAttrInfo().getAttrName());
            searchAttr.setAttrValue(sav.getBaseAttrValue().getValueName());
            return searchAttr;
        }).collect(Collectors.toList());

        goods.setAttrs(searchAttrList);

        goodsRepository.save(goods);
    }

    @Override
    public void lowerGoods(Long skuId) {

        goodsRepository.deleteById(skuId);

    }

    @Override
    public void hotScore(Long skuId) {
        //Key
        String hotScore = "hotScore";
        //
        Double score = redisTemplate.opsForZSet().incrementScore(hotScore, "skuId:" + skuId, 1);
        System.out.println("当前热点分数：" +score);
        if(score%10==0){
            //热点分数 增加10次 更新一次索引库
            System.out.println("满10当前热点分数：" +score);
            Optional<Goods> optional = goodsRepository.findById(skuId);
            Goods goods = optional.get();
            goods.setHotScore(Math.round(score));
            goodsRepository.save(goods);
        }
    }

    //搜索
    @Override
    public SearchResponseVo search(SearchParam searchParam) throws Exception {
        //1:构建搜索条件
        SearchRequest searchRequest = buildQueryDsl(searchParam);
        //2:执行查询
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //3:解析结果
        SearchResponseVo vo = parseResult(searchResponse);
        vo.setPageNo(searchParam.getPageNo());
        vo.setPageSize(searchParam.getPageSize());
        long totalPages = (vo.getTotal() + vo.getPageSize() - 1)/vo.getPageSize();
        vo.setTotalPages(totalPages);
        return vo;
    }

    //解析结果
    private SearchResponseVo parseResult(SearchResponse searchResponse) {
        SearchResponseVo vo = new SearchResponseVo();

        SearchHits hits = searchResponse.getHits();
        long totalHits = hits.totalHits;
        vo.setTotal(totalHits);

        List<Goods> goodsList = Arrays.stream(hits.getHits()).map(h -> {
            Goods goods = JSON.parseObject(h.getSourceAsString(), Goods.class);
            if(null != h.getHighlightFields().get("title")){
                goods.setTitle(h.getHighlightFields().get("title").fragments()[0].toString());
            }
            return goods;
        }).collect(Collectors.toList());

        vo.setGoodsList(goodsList);

        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();

        ParsedLongTerms tmIdAgg = (ParsedLongTerms)aggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> tmVoList = tmIdAgg.getBuckets().stream().map(t -> {
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            tmVo.setTmId(Long.parseLong(t.getKeyAsString()));
            Map<String, Aggregation> tmNameAggregationMap = t.getAggregations().asMap();
            ParsedStringTerms tmNameAgg = (ParsedStringTerms) tmNameAggregationMap.get("tmNameAgg");
            tmVo.setTmName(tmNameAgg.getBuckets().get(0).getKeyAsString());
            return tmVo;
        }).collect(Collectors.toList());

        vo.setTrademarkList(tmVoList);

        ParsedNested attrsAgg = (ParsedNested) aggregationMap.get("attrsAgg");

        ParsedLongTerms attrIdAgg = (ParsedLongTerms) attrsAgg.getAggregations().get("attrIdAgg");
        List<SearchResponseAttrVo> attrVoList = attrIdAgg.getBuckets().stream().map(attr -> {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            attrVo.setAttrId(Long.parseLong(attr.getKeyAsString()));

            Map<String, Aggregation> attrNameAggregationMap = attr.getAggregations().asMap();
            ParsedStringTerms attrNameAgg = (ParsedStringTerms) attrNameAggregationMap.get("attrNameAgg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());

            ParsedStringTerms attrValueAgg = (ParsedStringTerms) attrNameAggregationMap.get("attrValueAgg");
            List<String> attrValueList = attrValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            attrVo.setAttrValueList(attrValueList);
            return attrVo;
        }).collect(Collectors.toList());

        vo.setAttrsList(attrVoList);
        return vo;
    }




    private SearchRequest buildQueryDsl(SearchParam searchParam) {
        //构建搜索资源
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        searchSourceBuilder.query(boolQueryBuilder);

        String keyword = searchParam.getKeyword();
        if(!StringUtils.isEmpty(keyword)){
           boolQueryBuilder.must(QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        }
        String trademark = searchParam.getTrademark();
        if(!StringUtils.isEmpty(trademark)){
            String[] t = StringUtils.split(trademark, ":");
            if(t != null && t.length == 2){
                boolQueryBuilder.filter(QueryBuilders.termQuery("tmId",t[0]));
            }
        }

        Long category1Id = searchParam.getCategory1Id();
        if(null != category1Id){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id",category1Id.toString()));
        }
        Long category2Id = searchParam.getCategory2Id();
        if(null != category2Id){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id",category2Id.toString()));
        }
        Long category3Id = searchParam.getCategory3Id();
        if(null != category3Id){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id",category3Id.toString()));
        }
        String[] props = searchParam.getProps();
        if(null != props && props.length > 0){
            for (String prop : props) {
                String[] p = prop.split(":");
                System.out.println(p);
                if(p != null && p.length == 3){
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    boolQuery.must(QueryBuilders.termQuery("attrs.attrId",p[0]));
                    boolQuery.must(QueryBuilders.termQuery("attrs.attrValue",p[1]));
                    boolQueryBuilder.filter(
                            QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None));
                }
            }
        }
        //2 分页
        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();
        searchSourceBuilder.from((pageNo -1)*pageSize);
        searchSourceBuilder.size(pageSize);

        //3. 排序
        String order = searchParam.getOrder();
        if(!StringUtils.isEmpty(order)){
            String[] o = StringUtils.split(order, ":");
            if(null != o && o.length == 2){
                String field = null;
                switch (o[0]){
                    case "1":field="hotScore";break;
                    case "2":field="price";break;
                }
                searchSourceBuilder.sort(field,
                        StringUtils.endsWithIgnoreCase(o[1],"asc")?SortOrder.ASC:SortOrder.DESC);
            }
        }else{
            //默认 综合
            searchSourceBuilder.sort("hotScore",SortOrder.DESC);
        }
        //4：
        searchSourceBuilder.highlighter(new HighlightBuilder());
        HighlightBuilder highlighter = searchSourceBuilder.highlighter();
        highlighter.preTags("<font color='red'>").postTags("</font>").field("title");
        //5;
        searchSourceBuilder.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                  .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName")));

        searchSourceBuilder.aggregation(
                AggregationBuilders.nested("attrsAgg","attrs")
                        .subAggregation(
                                AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue")))
        );


        System.out.println(searchSourceBuilder.toString());

        //获取的域  或是排除的域
        searchSourceBuilder.fetchSource(new String[] {"id","title","defaultImg","price"},null);

        //返回搜索请求对象
        SearchRequest searchRequest = new SearchRequest("goods");
        searchRequest.types("info");
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }
    //构建搜索条件




}
